package com.pan.framework.core.factory;

import com.pan.framework.anno.aop.MyAfter;
import com.pan.framework.anno.aop.MyBefore;
import com.pan.framework.anno.aop.MyCut;
import com.pan.framework.anno.common.MyInject;
import com.pan.framework.core.container.MyContainer;
import com.pan.framework.core.entiy.CutInfo;
import com.pan.framework.core.factory.scan.MyScan;
import com.pan.framework.entity.BeanInfo;
import com.pan.framework.proxy.MyInvocationHandler;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.*;
import java.util.stream.Collectors;

public class MyBeanInit {

    private MyContainer container;

    private MyScan myScan;

    public MyBeanInit(MyContainer container, MyScan myScan) {
        this.container = container;
        this.myScan = myScan;
    }


    public void init() {
        List<String> allBeanName = (List<String>) container.getAllBeanName();
        allBeanName.forEach(name -> {
            //依赖注入
            this.beanInjectField(name);
            //TODO: 实现aop
            //proxyFunc(name);
            //
        });

    }

    @Deprecated
    //废弃 处理execution
    public void executionStrHandler(String execution) {
        String[] arr = execution.strip().split(";");
        List<CutInfo> resultList = new ArrayList<>();
        for (String s : arr) {

        }
        return;
    }

    @Deprecated
    //废弃
    public void proxyFunc(String name) {
        if (!container.exists(name)) {
            return;
        }
        BeanInfo bi = container.getBean(name);
        Class clazz = bi.getClazz();
        Object bean = bi.getBean();
        Method[] declaredMethods = bi.getClazz().getDeclaredMethods();
        Map<Method, MyCut> cutMethodMap = Arrays.stream(declaredMethods)
                .filter(method -> method.isAnnotationPresent(MyCut.class))
                .collect(Collectors.toMap(m -> m,
                        method -> method.getAnnotation(MyCut.class),
                        (k1, k2) -> k1,
                        HashMap::new));
        if (cutMethodMap.size() == 0) return;
        Object proxy = Proxy.newProxyInstance(
                myScan.getMyLoader(),
                clazz.getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        if (cutMethodMap.containsKey(method)) {
                            MyCut myCut = cutMethodMap.get(method);
                            String executionStr = myCut.execution();
                            //TODO :
                            List<CutInfo> ciList = null;//getCutInfo(executionStr);
                            for (int i = 0; i < ciList.size(); i++) {
                                CutInfo ci = ciList.get(i);
                                BeanInfo aspectInfo = container.getBean(ci.getAspectName());
                                Method aspectMethod = aspectInfo.getClazz()
                                        .getMethod(ci.getMethodName(), ci.getArgType());

                                if (aspectMethod.isAnnotationPresent(MyBefore.class)) {
                                    //before
                                    aspectMethod.invoke(aspectInfo.getBean(), args);
                                    return method.invoke(bean, args);
                                } else if (aspectMethod.isAnnotationPresent(MyAfter.class)) {
                                    //after

                                } else if (false) {
                                    // TODO: Around
                                    // ...
                                } else {
                                    throw new RuntimeException(aspectMethod.getName() + "没有指定切面方法的类型");
                                }
                            }
                            return null;
                        } else {
                            return method.invoke(bean, args);
                        }
                    }
                }
        );
        container.resetBean(new BeanInfo(name, clazz, proxy));
    }


    @Deprecated
    //解析 execution
    private List<CutInfo> getCutInfo(String execution) {
        //ex: "AspectClassName@MethodName(Param1Class,Param2Class, ...);..."
        String[] arr = execution.strip().split(";");
        List<CutInfo> resultList = new ArrayList<>();
        for (String s : arr) {
            CutInfo cutInfo = new CutInfo();
            String objAndMethod[] = s.split("@");
            cutInfo.setAspectName(objAndMethod[0]);

            String methodStr = objAndMethod[1].strip().substring(0, objAndMethod[1].length() - 1);
            String[] methodNameAndParams = methodStr.split("\\(");
            cutInfo.setMethodName(methodNameAndParams[0]);

            String paramStr = methodNameAndParams[1];
            String[] paramClassStr = paramStr.split(",");
            Class[] paramClasses = new Class[paramClassStr.length];
            for (int i = 0; i < paramClassStr.length; i++) {
                String className = paramClassStr[i];
                Class clazz;
                try {
                    clazz = Class.forName(className.strip());
                } catch (ClassNotFoundException e) {
                    throw new RuntimeException(e);
                }
                paramClasses[i] = clazz;
            }
            cutInfo.setArgType(paramClasses);
            resultList.add(cutInfo);
        }
        return resultList;
    }


    public void beanInjectField(String name) {
        if (!container.exists(name)) {
            return;
        }
        BeanInfo bi = container.getBean(name);
        Object targetBean = bi.getBean();
//        代理后的对象，不要直接用BeanInfo的Class和bean，代理后的对象，不是原来的对象只是，没有原来对象的Field，就会报错
        for (; Proxy.isProxyClass(targetBean.getClass()); ) {
            MyInvocationHandler invocationHandler = (MyInvocationHandler) Proxy.getInvocationHandler(targetBean);
            targetBean = invocationHandler.getTargetBean();
        }
        Field[] declaredFields = targetBean.getClass().getDeclaredFields();
        for (Field field : declaredFields) {
            if (field.isAnnotationPresent(MyInject.class)) {
                field.setAccessible(true);
                MyInject annotation = field.getAnnotation(MyInject.class);
                String injectName = annotation.name();
                if (!container.exists(injectName)) {
                    throw new RuntimeException(bi.getName() + "注入对象不存在：" + injectName);
                }
                Object injectObj = container.getBean(injectName).getBean();
                try {
//                    System.out.println("debug injectObject: "+injectObj);
//                    System.out.println("debug targetBean: "+targetBean);
//                    System.out.println("debug field: "+field);
                    field.set(targetBean, injectObj);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return;
    }
}
